home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue27 / flist200 / FLIST200.ZIP / filelist.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  1997-09-25  |  36.2 KB  |  1,015 lines

  1. {
  2.   FileList - File ListView components for Delphi 2
  3.   ⌐ Amedeo Lanza <amldc@tin.it>
  4.  
  5.  note for Delphi 3 users:
  6.   I did not test it with Delphi 3 nor NT, but some users reported me this
  7.   works fine with them.
  8.   I had a report (and fixed upon suggestion) of a warning message under NT (see
  9.   v2.0 release notes)
  10.   Also I had a report (for Delphi 3) of GPF starting the program with
  11.   the MultiSelect property set to true. I did not fix this as I tested it with
  12.   new version (and Delphi 2), and did not find such problem.
  13.  
  14. description:
  15.  TFileView is a ListView which links system image lists on creation and can
  16.  accept files dropped from Windows' Explorer.
  17.  TFileList is a FileView descendant wich encapsulates some system functions to
  18.  mix ancestor's capability with the FileListBox's Directory and Mask properties.
  19.  Thus specifying a path name and a file mask, the control will be filled with
  20.  the list of matching files, showing proper icons and a customizable set of file
  21.  informations (more than found in the Explores's file window).
  22.  
  23.  The reference section of this source was removed, so PLEASE SEE DOCUMENTATION
  24.  FOR REFERENCE about the controls in this source.
  25.  
  26. copyright & license
  27.  This source is Copyright ⌐ of Amedeo Lanza di Casalanza. You may freely use,
  28.  modify and distribute this software for non commercial purposes and for writing
  29.  freeware and/or shareware software. You MAY NOT SELL this software although you
  30.  may include it in software collection you distribute, provided there is no
  31.  charge for the software itself. Please leave the copyright information and any
  32.  additional information (readme.* ecc.) provided with the original author's
  33.  copy.
  34.  Use of this software for building of commercial programs should be expressely
  35.  authorized by the author.
  36.  Any use of this software for illegal activities is prohibited.
  37.  
  38.  If you enhance this software or fix any bug, please send the new source to
  39.  the author.
  40.  
  41. usage:
  42.   Drop a TFileList and some controls to handle Directory and File Mask
  43.   selections, then add some line of code to set the Directory and Mask
  44.   properties of TFileList upon selection from other controls ...just as you
  45.   where using a TFileListBox control :-)
  46.  
  47. dependencies:
  48.   Marcus Stephany's MASKSEARCH utility unit is needed. You may find it on DSP
  49.   in the MSTGREP.ZIP archive in Delphi 2.0 freeware section.
  50.  
  51. comments:
  52.   uses FindFirstFile, FindNextFile and FindClose API calls from Windows unit to
  53.   retrieve file list with better performances and ShGetFileInfo to retrieve
  54.   extended informations.
  55.   To find help about TWin32FindData look for WIN32_FIND_DATA in the Win32
  56.   Programmer's Reference and TWin32FindData in Windows unit (if you have the
  57.   RTL sources you can find it in the source\rtl\win directory.
  58.  
  59. current known limitations :
  60.   Tested only with Delphi 2.0 and Win95, requires anyway Win32s,Win95 or WinNT
  61.   as uses Win32 API calls.
  62.   Handles only File Items, System Resources cannot be displayed as in an
  63.   Explorer's window.
  64.   Columns order is fixed, and settings of columns' width and caption is quite
  65.   trivial; I hope I (or someone else) will be able to add a property editor for
  66.   that.
  67.  
  68. DISCLAIMER:
  69.  I ASK NO FEE and I GIVE NO WARRANTY for this software, either expressed or
  70.  implied. Use it AT YOUR OWN RISK.
  71.  
  72. suggestions, bug reports & comments to the author:
  73.  Amedeo Lanza di Casalanza
  74.  from: Torino, Italy
  75.  mailto:amldc@tin.it
  76.  http://volftp.tin.it/IT/IT/COLLABORAZIONI/LANZA/index.htm
  77.  
  78. WARNING FOR EMAIL ABUSE:
  79.  *BORLAND DELPHI* related messages are WELCOME, any other use of the author's
  80.  email address for unsolicited ADVERTISING is STRICTLY PROHIBITED.
  81.  
  82. aknowledgement:
  83.  Portions of this software come from freeware examples by
  84.   Markus Stephany
  85.   MirBir.St@T-Online.de
  86.   http://home.t-online.de/home/MirBir.St/
  87.  wich I found on Delphi Super Page (http://SunSITE.icm.edu.pl/delphi/)
  88.  
  89. also thanks for hints to the very good "Unofficial Newsletter of Delphi Users"
  90. (UNDU) at http://www.informant.com/undu/index.htm
  91.  
  92. history:
  93.  
  94.  v2.0 25-sep-1997 - amldc@tin.it
  95.    + Wrong item was reported when "\" leaking at end of used path. Fixed by
  96.      ensuring loaded path strings always end with a "\".
  97.    + Added check to prevent SortColumn being set to unused column.
  98.    + Splitted code, inserted TFileListView as ancestor and TFileView.
  99.      TFileListView embeds System Image List handling and File Drop
  100.    + AddFile is now a virtual method for both types and behave quite in a
  101.      different way: in TFileView provides to get the needed informations
  102.    + Added OnFileAdd property
  103.    + Added ability to get files dropped from Explorer
  104.    + Added fix to avoid warning message under WinNT when setting directory to an
  105.      empty drive (thanks to Sebastian Hildebrandt <hildebrandt@t0.or.at>)
  106.    + Updated for my modified version of MaskSearch (with case match handling).
  107.      This feature is not fully tested.
  108.  
  109.  v1.1 14-sep-1997 - amldc@tin.it
  110.    + Added SetupFileColumns in SetColWidth, needed if changing widths when
  111.      component visible.
  112.    + Modified SetDirectory to allow empty directory and skipping of directory
  113.      scan.
  114.    + Added support for directory list specification.
  115.    + Added usage of Marcus Stephany's MaskSearch unit (see dependencies note
  116.      above) for file search and support for file mask list specification.
  117.    + Added FileTypes property for file attribute based filtering
  118.    + DOS file name (cAlternateFilename) forced to Win file name (cFilename) when
  119.      returned empty (in Win32FindData) from FindFirstFile and FindNextFile.
  120.    + Added fiDosExt column (mainly for DOS extension based sorting)
  121.    + Added fiAll (display all columns) and modified default set of columns to
  122.      reflect the Explorer's one.
  123.  
  124.  v1.0 10-aug-1997 - amldc@tin.it
  125.    + first development
  126.  
  127. }
  128.  
  129. unit FileList;
  130.  
  131. interface
  132.  
  133. uses
  134.   Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  135.   ComCtrls, ShellApi, MaskSearch;
  136.  
  137. type
  138.  
  139.   /////////////////////
  140.   // TCustomFileView //
  141.   /////////////////////
  142.  
  143.   // callback for Drop file
  144.   TFileDropEvent = procedure(Files: Tstrings; X, Y: Integer) of object;
  145.   // System ListView
  146.  
  147.   TFileListView = class(TCustomListView)
  148.   private
  149.     FAcceptFiles: Boolean;
  150.     FOnFileDrop: TFileDropEvent;
  151.     procedure GetSystemImageList; // load LargeImages and SmallImages
  152.     procedure WMDROPFILES(var Msg: TWMDropFiles); message WM_DROPFILES;
  153.     procedure SetAcceptFiles(Accept: Boolean);
  154.   protected
  155.     { Protected declarations }
  156.     constructor Create (aOwner :TComponent); override;
  157.     destructor Destroy; override;
  158.     procedure Loaded; override;
  159.   public
  160.     function AddFile (FileName :TFileName) :TListItem; virtual;
  161.   published
  162.     { Published inherited declarations }
  163.     property Align;
  164.     property BorderStyle;
  165.     property Color;
  166.     property ColumnClick;
  167.     property OnClick;
  168.     property OnDblClick;
  169.     property Ctl3D;
  170.     property DragMode;
  171.     property ReadOnly;
  172.     property Font;
  173.     property HideSelection;
  174.     property IconOptions;
  175.     property Items;
  176.     property AllocBy;
  177.     property MultiSelect;
  178.     property OnChange;
  179.     property OnChanging;
  180.     property OnColumnClick;
  181.     property OnCompare;
  182.     property OnDeletion;
  183.     property OnEdited;
  184.     property OnEditing;
  185.     property OnEnter;
  186.     property OnExit;
  187.     property OnInsert;
  188.     property OnDragDrop;
  189.     property OnDragOver;
  190.     property DragCursor;
  191.     property OnStartDrag;
  192.     property OnEndDrag;
  193.     property OnMouseDown;
  194.     property OnMouseMove;
  195.     property OnMouseUp;
  196.     property ParentShowHint;
  197.     property ShowHint;
  198.     property PopupMenu;
  199.     property ShowColumnHeaders;
  200.     property SortType;
  201.     property TabOrder;
  202.     property TabStop default True;
  203.     property ViewStyle;
  204.     property Visible;
  205.     property OnKeyDown;
  206.     property OnKeyPress;
  207.     property OnKeyUp;
  208.     {class specific published properties}
  209.     property AcceptFiles: Boolean read FAcceptFiles write SetAcceptFiles;
  210.     property OnFileDrop: TFileDropEvent read FOnFileDrop write FOnFileDrop;
  211.   end;
  212.  
  213.   // Just exports the needed inherited properties
  214.   TFileView = class(TFileListView)
  215.   published
  216.     { Published inherited declarations }
  217.     property Columns;
  218.   end;
  219.  
  220.   ///////////////
  221.   // TFileList //
  222.   ///////////////
  223.  
  224.   // specialized list for patterns handling
  225.   TPatternList = class (TStringList)
  226.   public
  227.     procedure AddPatterns(S :string);
  228.   end;
  229.  
  230.   // file attributes
  231.   TFileType =({ftDirectory,}ftArchive,ftReadonly,
  232.               ftSystem,ftHidden,{ftCompressed,}ftTemporary,ftAll);
  233.   TFileTypes = Set of TFileType;
  234.  
  235.   // file informations (display columns)
  236.   TFileInfo = (fiName,fiSize,fiType,
  237.                fiModified,fiCreated,fiLastAccess,
  238.                fiAttributes,fiDosName,fiDosExt,fiPath,fiAll);
  239.   TFileInfos = Set of TFileInfo;
  240.  
  241.   // Column settings structure
  242.   TColumnInfo = record
  243.     Caption   :TCaption;
  244.     Width     :integer;
  245.     Alignment :TAlignment;
  246.     Column    :integer;
  247.   end;
  248.   TColumnInfos = array [TFileInfo] of TColumnInfo;
  249.   TInfoColumn = array [0..Ord(Pred(fiPath))] of TFileInfo;
  250.  
  251.  
  252.   // callback for items insertion
  253.   TFlInsertEvent = procedure (Sender: TObject; Item: TListItem; Path: string; FindData :TWin32FindData) of object;
  254.  
  255.   // TFILELIST class
  256.   TFileList = class(TFileListView)
  257.   private
  258.     { Private declarations }
  259.     fFileTypes  :TFileTypes;        // file attributes
  260.     fDirectory  :TPatternList;      // current directory
  261.     fFileMask   :TStringList;       // file specifications list
  262.     fDisplayDir   :boolean;         // include directories in file scan
  263.     fViewColumns  :TFileInfos;      // visible columns
  264.     fSortColumn   :TFileInfo;       // current sort column
  265.     fOnFileAdd    :TFLInsertEvent;  // chance to add custom info
  266.     procedure UpdateFileList;       // scan the directory and load file list
  267.     procedure SetupFileColumnInfos; // dynamic creation of columns
  268.   protected
  269.     { Protected declarations }
  270.     procedure ColClick(Column: TListColumn); override;
  271.     procedure Loaded; override;
  272.     function  GetMask :string;  // Get file specifications
  273.     function  GetPath :string;  // Get search path list
  274.     procedure SetDisplayDir  (Display :boolean);  // enable/disable list of dirs
  275.     procedure SetFileTypes (FT :TFileTypes);
  276.     procedure SetMask (Mask :string); // Set file specifications
  277.     procedure SetPath (Path :string); // Set search path specifications
  278.     procedure SetSortColumn  (Column :TFileInfo);
  279.     procedure SetupFileColumns;
  280.     procedure SetViewColumns (Columns :TFileInfos);
  281.     procedure AddFileData (NewItem :TListItem; Path :string; FindData :TWin32FindData); // add a file
  282.   public
  283.     { Public declarations }
  284.     constructor Create (aOwner :TComponent); override;
  285.     destructor Destroy; override;
  286.     function  AddFile   (FileName :TFileName) :TListItem; override; // add a file
  287.     procedure AddMask   (Mask :string); // Add file specifications
  288.     procedure AddPath   (Path :string); // Add search path specifications
  289.     procedure SetColCaption (Col: TFileInfo; Value :string); // set column caption
  290.     procedure SetColWidth   (Col: TFileInfo; Value :integer); // set column width
  291.   published
  292.     {class specific published properties}
  293.     property Directory :string read GetPath write SetPath;
  294.     property FileTypes :TFileTypes read fFileTypes write SetFileTypes;
  295.     property DisplayDirectory :boolean read fDisplayDir write SetDisplayDir default true;
  296.     property Mask :string read GetMask write SetMask;
  297.     property OnFileAdd :TFLInsertEvent read fOnfileAdd write fOnFileAdd;
  298.     property SortColumn :TFileInfo read fSortColumn write SetSortColumn default fiName;
  299.     property ViewColumns :TFileInfos read fViewColumns write SetViewColumns;
  300.   end;
  301.  
  302. procedure Register;
  303.  
  304. implementation
  305.  
  306. var
  307.   InvertSort :boolean; // used to invert sorting when column clicked twice
  308.   fColumnInfos  :TColumnInfos;  // columns informations
  309.   fInfoColumn   :TInfoColumn;   // columns informations
  310.  
  311. ////////////////////////////////////////////////////////////////////////////////
  312. // TPATTERNLIST
  313. ////////////////////////////////////////////////////////////////////////////////
  314.  
  315. // add patterns from the specified string
  316. // in format aaa;bbb;ccc ecc.
  317. procedure TPatternList.AddPatterns(S :string);
  318. var
  319.   Pattern :string;
  320.   Rest    :string;
  321.   P :integer;
  322. begin
  323.   if S = '' then Exit; // avoid empty patterns
  324.   Rest := S;
  325.   Pattern := '';
  326.   P := Pos(';',Rest);
  327.   while (Rest <> '') and (P > 0) do
  328.   begin
  329.     Pattern := Copy(Rest,1,P-1);
  330.     Rest    := Copy(Rest,P+1,Length(Rest));
  331.     // ensure all paths have an ending backslash
  332.     if Pattern[Length(Pattern)] <> '\' then Pattern := Pattern + '\';
  333.     Add(Pattern);
  334.     P := Pos(';',Rest);
  335.   end;
  336.   if Rest[Length(Rest)] <> '\' then Rest := Rest + '\';
  337.   Add(Rest);
  338. end;
  339.  
  340.  
  341. ////////////////////////////////////////////////////////////////////////////////
  342. // UTILITIES
  343. ////////////////////////////////////////////////////////////////////////////////
  344.  
  345. //
  346. // Sorting Routine
  347. //
  348. function SortProc (Item1,Item2 :TListItem; ColIndex :integer) :integer; stdcall;
  349. var
  350.   Caption1,Caption2,
  351.   String1,String2,
  352.   Attributes1,Attributes2 :string;
  353. begin
  354.   Result := 0; // Defaults to equal
  355.   // Tests Column[1] (Size) to force Directories before Files
  356.   if (Item1.SubItems[0] = '') and (Item2.SubItems[0] <> '') then Result := -1
  357.   else
  358.     // Tests Column[1] (Size) to force Directories before Files
  359.     if (Item1.SubItems[0] <> '') and (Item2.SubItems[0] = '') then Result := 1
  360.     else
  361.     //
  362.     // both items are directory or file
  363.     //
  364.     begin
  365.       // if ColIndex is 0, just a sort by NAME is required
  366.       if ColIndex < 1 then
  367.       begin
  368.         // Converts NAME to uppercase to ignore case
  369.         Caption1 := AnsiUpperCase(Item1.Caption);
  370.         Caption2 := AnsiUpperCase(Item2.Caption);
  371.         // Compare NAMES
  372.         if Caption1 > Caption2 then Result := 1 else
  373.         if Caption1 < Caption2 then Result := -1;
  374.       end
  375.       else
  376.       begin
  377.         // checks for invalid column specified (1st item)
  378.         if Item1.SubItems.Count < ColIndex then String1 := ''
  379.         else String1 := AnsiUpperCase(Item1.SubItems[ColIndex-1]);
  380.         // checks for invalid column specified (2nd item)
  381.         if Item2.SubItems.Count < ColIndex then String2 := ''
  382.         else String2 := AnsiUpperCase(Item2.SubItems[ColIndex-1]);
  383.  
  384.         // compare the selected values
  385.         if String1 > String2 then Result := 1 else
  386.           if String1 < String2 then Result := -1 else
  387.           {if String1 = String2 then} // stings are equal, try to sort on Caption
  388.             begin
  389.               // Converts NAME to uppercase to ignore case
  390.               Caption1 := AnsiUpperCase(Item1.Caption);
  391.               Caption2 := AnsiUpperCase(Item2.Caption);
  392.               // Compare NAMES
  393.               if Caption1 > Caption2 then Result := 1 else
  394.               if Caption1 < Caption2 then Result := -1
  395.             end;
  396.       end;
  397.     // invert Sort if requested
  398.     if InvertSort then Result := Result * -1; // is sort reverted ?
  399.     // Date-Time field sorted in reverse order
  400.     if fColumnInfos[fiCreated].Column = ColIndex then Result := Result * -1
  401.     else if fColumnInfos[fiModified].Column = ColIndex then Result := Result * -1
  402.     else if fColumnInfos[fiLastAccess].Column = ColIndex then Result := Result * -1;
  403.   end;
  404. end;
  405.  
  406. ////////////////////////////////////////////////////////////////////////////////
  407. // STRING ROUTINES
  408. ////////////////////////////////////////////////////////////////////////////////
  409.  
  410. // Get Shell Info for the specified file
  411. // procedure GetShellFileInfo (FileName :TFileName; var ShFileInfo :TShFileInfo);
  412. // begin
  413. //  ShGetFileInfo (PChar(FileName),0,ShFileInfo,SizeOf (ShFileInfo),
  414. //                 shgfi_SysIconIndex or shgfi_Icon or
  415. //                 shgfi_DisplayName or shgfi_TypeName or
  416. //                 shgfi_SmallIcon);
  417. // end;
  418.  
  419. // Convert Size for Sort
  420. function SizeStr (Size,Typ :integer ) :string;
  421. begin
  422.   if (Typ and file_attribute_Directory) = file_attribute_Directory then
  423.     Result := ''
  424.   else
  425.     Result := Format ('%10d',[Size]);
  426. end;
  427.  
  428. // returns a string with file attributes (DRSH)
  429. function AttrStr(Attr:integer):string;
  430. begin
  431.   Result := '';
  432.   if (Attr and file_attribute_Directory)  > 0 then Result := Result + 'D';
  433.   if (Attr and file_attribute_Archive)    > 0 then Result := Result + 'A';
  434.   if (Attr and file_attribute_Readonly)   > 0 then Result := Result + 'R';
  435.   if (Attr and file_attribute_System)     > 0 then Result := Result + 'S';
  436.   if (Attr and file_attribute_Hidden)     > 0 then Result := Result + 'H';
  437. //  if (Attr and FILE_ATTRIBUTE_COMPRESSED) > 0 then Result := Result + 'C';
  438.   if (Attr and file_attribute_Temporary)  > 0 then Result := Result + 'T';
  439. end;
  440.  
  441. // File Date & Time
  442. function GetLocalTime(a:tfiletime):string;
  443. // This function retrieves the last time, the given file was written to disk
  444. var
  445.   mtm :TSystemTime;
  446.   at  :TFileTime;
  447.   ds,ts:ShortString;
  448. begin
  449.   // Time must get converted, else there is an error of one hour
  450.   // Does anybody know what this function does ?
  451.   // Maybe something like summertime/wintertime (or what you call it out of Germany) ?
  452.   filetimetolocalfiletime(a,at);
  453.   filetimetosystemtime(at,mtm);
  454.   SetLength(ds, GetDateFormat(LOCALE_USER_DEFAULT, 0, @mtm, NIL, @ds[1], 255) - 1);
  455.   SetLength(ts, GetTimeFormat(LOCALE_USER_DEFAULT, time_noseconds, @mtm, NIL,
  456.                                                @ts[1], 255)  - 1);
  457.   Result:=ds+'  '+ts;
  458. end; // End getmod
  459.  
  460. ////////////////////////////////////////////////////////////////////////////////
  461. // CLASS METHODS FOR TSystemFileList
  462. ////////////////////////////////////////////////////////////////////////////////
  463. constructor TFileListView.Create (aOwner :TComponent);
  464. begin
  465.   inherited Create (aOwner);
  466.   FAcceptFiles := False;
  467.   GetSystemImageList;    // get system icon list
  468. end;
  469.  
  470. destructor TFileListView.Destroy;
  471. begin
  472.   // SmallImages.Free; // needed ?
  473.   // LargeImages.Free; // needed ?
  474.   inherited Destroy;
  475. end;
  476. procedure TFileListView.Loaded;
  477. begin
  478.   inherited Loaded;
  479.   if not (csDesigning in ComponentState) then
  480.     DragAcceptFiles(Handle, FAcceptFiles);
  481. end;
  482.  
  483. procedure TFileListView.WMDROPFILES(var Msg: TWMDropFiles);
  484. var
  485.   i, DropCount, BufSize: integer;
  486.   FileName: pChar;
  487.   FileList: TStrings;
  488.   Point: TPoint;
  489. begin
  490.   BufSize := 0;
  491.   DropCount := DragQueryFile(Msg.Drop, $FFFFFFFF, nil, BufSize);
  492.   FileList := TStringList.Create;
  493.   try
  494.   for i := 0 to DropCount - 1 do begin
  495.     BufSize := DragQueryFile(Msg.Drop, i, nil, BufSize) + 1;
  496.     FileName := StrAlloc(BufSize + 1);
  497.     try
  498.       DragQueryFile(Msg.Drop, i, FileName, BufSize);
  499.       FileList.Add(FileName);
  500.       DragQueryPoint(Msg.Drop, Point);
  501.     finally
  502.       StrDispose(FileName);
  503.     end;
  504.   end;
  505.   DragFinish(Msg.Drop);
  506.   if Assigned(FOnFileDrop) then
  507.     FOnFileDrop(FileList, Point.X, Point.Y)
  508.   finally
  509.     FileList.Free;
  510.   end;
  511. end;
  512.  
  513. procedure TFileListView.setAcceptFiles(Accept: Boolean);
  514. begin
  515.   if not (csDesigning in ComponentState) then
  516.     DragAcceptFiles(Handle, Accept);
  517.   FAcceptFiles := Accept;
  518. end;
  519.  
  520. // Get System Image List
  521. procedure TFileListView.GetSystemImageList;
  522. var
  523.   SysImageList :uint;      // temporary handle for System ImageLists
  524.   ShFileInfo :TShFileInfo; // Shell File Info structure
  525. begin
  526.   LargeImages := TImageList.Create(self);
  527.   SysImageList := ShGetFileInfo('',0,ShFileInfo,SizeOf(ShFileInfo),shgfi_SysIconIndex or shgfi_LargeIcon);
  528.   if SysImageList <> 0 then
  529.   begin
  530.     LargeImages.Handle := SysImageList;
  531.     LargeImages.ShareImages := true; // Avoid freeing of System Image List !
  532.   end;
  533.   SmallImages := TImageList.Create(self);
  534.   SysImageList := ShGetFileInfo('',0,ShFileInfo,SizeOf(ShFileInfo),shgfi_SysIconIndex or shgfi_SmallIcon);
  535.   if SysImageList <> 0 then
  536.   begin
  537.     SmallImages.Handle := SysImageList;
  538.     SmallImages.ShareImages := true; // Avoid freeing of System Image List !
  539.   end;
  540. end;
  541.  
  542. // adds a file and returns the newly added item
  543.  
  544. function TFileListView.AddFile (FileName :TFileName) :TListItem;
  545. var
  546.   ShFileINfo :TShFileInfo;
  547. begin
  548.   Result := Items.Add;
  549.   // Get Windows file name and system icon index
  550.   ShGetFileInfo (PChar(FileName),0,ShFileInfo,SizeOf (ShFileInfo),
  551.                  shgfi_SysIconIndex or shgfi_Icon or
  552.                  shgfi_DisplayName  or shgfi_SmallIcon);
  553.   Result.Caption := ShFileInfo.szDisplayName; // Set the item caption
  554.   Result.ImageIndex := ShFileInfo.IIcon;      // Set file icon index
  555. end;
  556.  
  557. ////////////////////////////////////////////////////////////////////////////////
  558. // CLASS METHODS FOR TFileList
  559. ////////////////////////////////////////////////////////////////////////////////
  560.  
  561. // Object creation
  562. constructor TFileList.Create (aOwner :TComponent);
  563. begin
  564.   inherited Create(aOwner);
  565.   fDisplayDir := true;   // include directory in display
  566.   fFileTypes  := [ftArchive,ftReadonly]; // default searched file type
  567.   fSortColumn := fiName; // set default sort to Name
  568.   fViewColumns := [fiName,fiSize,fiType,fiModified]; // columns useb by Explorer
  569.  
  570.   SetupFileColumnInfos;  // load column configuration
  571.   InvertSort := false;   // default is normal sort
  572.  
  573.   fDirectory := TPatternList.Create;   // Search Path list
  574.   fDirectory.Sorted := true;          // sorted and
  575.   fDirectory.Duplicates := dupIgnore; // ignore duplicates
  576.  
  577.   fFileMask := TStringList.Create;     // File Specifications list
  578.   fFileMask.Sorted := true;            // sorted and
  579.   fFileMask.Duplicates := dupIgnore;   // ignore duplicates
  580.   fFileMask.Add('*.*');
  581. end;
  582.  
  583. procedure TFileList.Loaded;
  584. begin
  585.   inherited Loaded;
  586.   SetupFileColumns;     // build listview columns
  587. end;
  588.  
  589. // Object deletion
  590. destructor TFileList.Destroy;
  591. begin
  592.   fFileMask.Free;      // releases child component
  593.   fDirectory.Free;    // releases child component
  594.   inherited Destroy;
  595. end;
  596. ////////////////////////////////////////////////////////////////////////////////
  597. // INITIALIZATION
  598. ////////////////////////////////////////////////////////////////////////////////
  599.  
  600. procedure TFileList.SetColCaption (Col: TFileInfo; Value :string);
  601. begin
  602.   if fColumnInfos[Col].Caption = Value then Exit;
  603.   fColumnInfos[Col].Caption := Value;
  604. end;
  605. procedure TFileList.SetColWidth (Col: TFileInfo; Value :integer);
  606. begin
  607.   if fColumnInfos[Col].Width = Value then Exit;
  608.   fColumnInfos[Col].Width := Value;
  609.   SetupFileColumns; // V1.1: needed if changing widths when component visible
  610. end;
  611.  
  612. // Sets up columns infos for ListView
  613. procedure TFileList.SetupFileColumnInfos;
  614. begin
  615.   // column properties for NAME
  616.   fColumnInfos[fiName].Caption := 'Name';
  617.   fColumnInfos[fiName].Width := 150;
  618.   fColumnInfos[fiName].Alignment := taLeftJustify;
  619.   // column properties for SIZE
  620.   fColumnInfos[fiSize].Caption := 'Size';
  621.   fColumnInfos[fiSize].Width := 60;
  622.   fColumnInfos[fiSize].Alignment := taRightJustify;
  623.   // column properties for Type
  624.   fColumnInfos[fiType].Caption := 'Type';
  625.   fColumnInfos[fiType].Width := 130;
  626.   fColumnInfos[fiType].Alignment := taLeftJustify;
  627.   // column properties for Modified
  628.   fColumnInfos[fiModified].Caption := 'Modified';
  629.   fColumnInfos[fiModified].Width := 100;
  630.   fColumnInfos[fiModified].Alignment := taLeftJustify;
  631.   // column properties for Created
  632.   fColumnInfos[fiCreated].Caption := 'Created';
  633.   fColumnInfos[fiCreated].Width := 100;
  634.   fColumnInfos[fiCreated].Alignment := taLeftJustify;
  635.   // column properties for Last Access
  636.   fColumnInfos[fiLastAccess].Caption := 'Last Access';
  637.   fColumnInfos[fiLastAccess].Width := 85;
  638.   fColumnInfos[fiLastAccess].Alignment := taLeftJustify;
  639.   // column properties for Attributes
  640.   fColumnInfos[fiAttributes].Caption := 'Attributes';
  641.   fColumnInfos[fiAttributes].Width := 60;
  642.   fColumnInfos[fiAttributes].Alignment := taLeftJustify;
  643.   // column properties for DosName
  644.   fColumnInfos[fiDosName].Caption := 'Dos Name';
  645.   fColumnInfos[fiDosName].Width := 130;
  646.   fColumnInfos[fiDosName].Alignment := taLeftJustify;
  647.   // column properties for DosName
  648.   fColumnInfos[fiDosExt].Caption := 'Dos Ext';
  649.   fColumnInfos[fiDosExt].Width := 60;
  650.   fColumnInfos[fiDosExt].Alignment := taLeftJustify;
  651.   // column properties for Path
  652.   fColumnInfos[fiPath].Caption := 'Path';
  653.   fColumnInfos[fiPath].Width := 200;
  654.   fColumnInfos[fiPath].Alignment := taLeftJustify;
  655. end;
  656. // Builds columns for ListView
  657. // NAME and SIZE column always created and just hidden if not
  658. // required. Other columns are created only if requested
  659. procedure TFileList.SetupFileColumns;
  660. var
  661.   Ind :integer;
  662.   Inf :TFileInfo;
  663. begin
  664.   with Columns do
  665.   begin
  666.     Clear;
  667.     // NAME
  668.     with Add do // adds and sets up NAME column
  669.     begin
  670.       Caption   := fColumnInfos[fiName].Caption;
  671.       Alignment := fColumnInfos[fiName].Alignment;
  672.       fColumnInfos[fiName].Column    := 0; // set column index;
  673.       if (fiName in fViewColumns) or (fiAll in fViewColumns) then
  674.         Width := fColumnInfos[fiName].Width
  675.       else
  676.         Width := 0; // Hide column if not required
  677.       fInfoColumn[0] := fiName;
  678.     end;
  679.     with Add do // adds and sets up SIZE column
  680.     begin
  681.       Caption   := fColumnInfos[fiSize].Caption;
  682.       Alignment := fColumnInfos[fiSize].Alignment;
  683.       fColumnInfos[fiSize].Column    := 1; // set column index;
  684.       if (fiSize in fViewColumns) or (fiAll in fViewColumns) then
  685.         Width := fColumnInfos[fiSize].Width
  686.       else
  687.         Width := 0; // Hide column if not required
  688.       fInfoColumn[1] := fiSize;
  689.     end;
  690.     // all remaining columns
  691.     Ind := 2; // start from column 2 (3rd column)
  692.     for Inf := fiType  to Pred(fiAll) do
  693.       if (Inf in fViewColumns) or (fiAll in fViewColumns) then
  694.       begin
  695.         with Add do // adds and sets up SIZE column
  696.         begin
  697.           Caption   := fColumnInfos[Inf].Caption;
  698.           Alignment := fColumnInfos[Inf].Alignment;
  699.           Width     := fColumnInfos[Inf].Width;
  700.           fColumnInfos[Inf].Column := Ind; // set column index;
  701.           // set
  702.           fInfoColumn[Ind] := Inf;
  703.           Inc(Ind);
  704.         end;
  705.       end
  706.       else
  707.         fColumnInfos[Inf].Column := -1; // reset column index if column not used
  708.   end; {with columns do}
  709. end;
  710.  
  711. ////////////////////////////////////////////////////////////////////////////////
  712. // EVENT HANDLERS
  713. ////////////////////////////////////////////////////////////////////////////////
  714.  
  715. // overrides ancestor's handler for column headers click:
  716. //  calls first the Set Sort Column procedure and then
  717. //  the inherited ColClick method.
  718. procedure TFileList.ColClick(Column: TListColumn);
  719. begin
  720.   SetSortColumn(fInfoColumn[Column.Index]);
  721.   inherited ColClick(Column);
  722. end;
  723.  
  724. ////////////////////////////////////////////////////////////////////////////////
  725. // PROPERTIES INTERFACE
  726. ////////////////////////////////////////////////////////////////////////////////
  727.  
  728. // Set File Attributes
  729. procedure TFileList.SetFileTypes (FT :TFileTypes);
  730. begin
  731.  if FT = fFileTypes then Exit;
  732.  fFileTypes := FT;
  733.  UpdateFileList;
  734. end;
  735.  
  736. // Set File Specification
  737. procedure TFileList.SetMask (Mask :string);
  738. begin
  739.   SetFilters (Mask,fFileMask,true,false);
  740.   UpdateFileList; // Update File List View
  741. end;
  742. // Add specifications to File Specifications list
  743. procedure TFileList.AddMask (Mask :string);
  744. begin
  745.   SetFilters(GetMask + ';' + Mask,fFileMask,true,false);
  746. //  UpdateFileList; // Update File List View
  747. end;
  748. // Get File Specifications list
  749. function TFileList.GetMask :string;
  750. var
  751.   I :integer;
  752. begin
  753.   Result := ''; // Default result to ''
  754.   for I := 0 to Pred(fFileMask.Count) do
  755.     Result := Result + fFileMask[I]  + ';';
  756.   if Result[Length(Result)] = ';' then // remove last ';'
  757.     Result := Copy(Result,1,Length(Result)-1);
  758. end;
  759.  
  760. // Set Search Path List
  761. procedure TFileList.SetPath (Path :string);
  762. var
  763.   I :integer;
  764. begin
  765.   fDirectory.Clear; // Clear search path list
  766.   fDirectory.AddPatterns(Path);
  767.   UpdateFileList; // Update File List View
  768. end;
  769. // Add specifications to Search Path List
  770. procedure TFileList.AddPath (Path :string);
  771. begin
  772.   fDirectory.Addpatterns(Path);
  773.   UpdateFileList; // Update File List View
  774.   // modificare per caricre solo da Path
  775. end;
  776. // Get Search Path List
  777. function TFileList.GetPath :string;
  778. var
  779.   I :integer;
  780. begin
  781.   Result := ''; // Default result to ''
  782.   if fDirectory.Count < 1 then Exit;
  783.   for I := 0 to Pred(fDirectory.Count) do
  784.     Result := Result + fDirectory[I]  + ';';
  785.   if Result[Length(Result)] = ';' then // remove last ';'
  786.     Result := Copy(Result,1,Length(Result)-1);
  787. end;
  788.  
  789. // Set current column set
  790. procedure TFileList.SetViewColumns (Columns :TFileInfos);
  791. begin
  792.   if Columns = fViewColumns then Exit;
  793.   fViewColumns := Columns;
  794.   if ViewStyle = vsReport then
  795.   begin
  796.     SetupFileColumns;      // rebuild listview columns
  797.     UpdateFileList;
  798.   end;
  799. end;
  800.  
  801. // Set current sort column
  802. procedure TFileList.SetSortColumn (Column :TFileInfo);
  803. begin
  804.   if not ((Column in fViewColumns) or (fiAll in fViewColumns)) then
  805.     Exit; // prevent unused columns to be set for sorting
  806.   if Column = fiAll then
  807.     Exit; // fiAll is not a valid sort column 
  808.   // invert sorting if the specified column was already selected
  809.   if Column = SortColumn then InvertSort := not InvertSort
  810.   else
  811.     fSortColumn := Column;
  812.   if ViewStyle = vsReport then
  813.   begin
  814.     CustomSort(@SortProc,fColumnInfos[SortColumn].Column); // Sorts on 0 based column index
  815.   end;
  816. end;
  817.  
  818. // Allows/Prevent loading of Directory Items in the file list
  819. procedure TFileList.SetDisplayDir (Display :boolean);
  820. begin
  821.   if Display = fDisplayDir then Exit;
  822.   fDisplayDir := Display;
  823.   UpdateFileList;         // update file list
  824. end;
  825.  
  826.  
  827. ////////////////////////////////////////////////////////////////////////////////
  828. // PROCESSING & SERVICES ROUTINES
  829. ////////////////////////////////////////////////////////////////////////////////
  830. function TFileList.AddFile (FileName :TFileName) :TListItem;
  831. var
  832.   Ret :boolean;
  833.   FindHandle :THandle;
  834.   FindData :TWin32FindData;
  835.   S :string;
  836. begin
  837.   Result := inherited AddFile(FileName);
  838.   FindHandle := Windows.FindFirstFile(PChar(FileName),FindData);
  839.   try // get file informations
  840.     if (FindHandle <> INVALID_HANDLE_VALUE) then
  841.     begin
  842.       S := ExtractFilePath(FileName);
  843.       if S[Length(S)] <> '\' then
  844.         S := S + '\';
  845.       AddFileData(Result,S,FindData);
  846.     end;
  847.   finally
  848.     Windows.FindClose(FindHandle);
  849.  end;
  850. end;
  851.  
  852. // Add a file entry to the list view. The FindData structure should be
  853. // returned from a FindFirstFile/FindNextFile call
  854.  
  855. procedure TFileList.AddFileData (NewItem :TListItem; Path :string; FindData :TWin32FindData);
  856. var
  857.   ShFileInfo:TShFileInfo;
  858.    // compute file size
  859.   function FileSize (hi,lo: integer) :longint;
  860.   begin
  861.     Result := (hi * MAXDWORD) + lo;
  862.   end;
  863.   // actually adds subitems
  864.   procedure AddInfo;
  865.   var
  866.     S :string;
  867.   begin
  868.     with FindData do
  869.     begin
  870.       // SIZE
  871.       NewItem.SubItems.Add (SizeStr(FileSize(nFileSizeHigh,nFileSizeLow),dwFileAttributes));
  872.       // TYPE
  873.       if (fiType in fViewColumns) or (fiAll in fViewColumns) then
  874.       begin
  875.         ShGetFileInfo (PChar(Path + cFileName),0,ShFileInfo,SizeOf (ShFileInfo),
  876.                        shgfi_DisplayName or shgfi_TypeName);
  877.         NewItem.SubItems.Add (ShFileInfo.szTypeName); //type
  878.       end;
  879.       // MODIFIED
  880.       if (fiModified in fViewColumns) or (fiAll in fViewColumns) then
  881.         NewItem.SubItems.Add (GetLocalTime(ftLastWriteTime));
  882.       // CREATION
  883.       if (fiCreated in fViewColumns) or (fiAll in fViewColumns) then // add subitem only if requested
  884.         NewItem.SubItems.Add (GetLocalTime(ftCreationTime));
  885.       // LAST ACCESS
  886.       if (fiLastAccess in fViewColumns) or (fiAll in fViewColumns) then // add subitem only if requested
  887.         NewItem.SubItems.Add (GetLocalTime(ftLastAccessTime));
  888.       // ATTRIBUTES
  889.       if (fiAttributes in fViewColumns) or (fiAll in fViewColumns) then // add subitem only if requested
  890.         NewItem.SubItems.Add (AttrStr(dwFileAttributes));
  891.       // DOS NAME
  892.       S := cAlternateFileName;
  893.       if S = '' then S := cFileName; // avoid empty DOS name
  894.       if (fiDosName in fViewColumns) or (fiAll in fViewColumns) then // add subitem only if requested
  895.       begin
  896.         NewItem.SubItems.Add (S);
  897.       end;
  898.       // DOS EXTENSION
  899.       S := cAlternateFileName;
  900.       if S = '' then S := cFileName; // avoid empty DOS name
  901.       if (fiDosName in fViewColumns) or (fiAll in fViewColumns) then // add subitem only if requested
  902.       begin
  903.         NewItem.SubItems.Add (ExtractFileExt(S));
  904.       end;
  905.     end; {with FindData}
  906.     // PATH
  907.     if (fiPath in fViewColumns) or (fiAll in fViewColumns) then // add subitem only if requested
  908.       NewItem.SubItems.Add (Path);
  909.       // user callback for OnAddFile
  910.   end;
  911.  
  912. begin // AddFileData main
  913.   AddInfo;                                        // adds file informations
  914.   if Assigned(fOnFileAdd) then fOnFileAdd(Self,NewItem,Path,FindData);
  915. end;
  916.  
  917. function CheckAttributes(Att :DWord; Typ :TFileTypes) :boolean;
  918. begin
  919.   if  (ftAll in Typ) then Result := true
  920.   else
  921.   begin
  922.     Result := true;
  923.     if (Att and file_attribute_Archive) = file_attribute_Archive then
  924.       Result := Result and (ftArchive in Typ);
  925.     if (Att and file_attribute_Readonly) = file_attribute_Readonly then
  926.       Result := Result and (ftArchive in Typ);
  927.     if (Att and file_attribute_Hidden) = file_attribute_Hidden then
  928.       Result := Result and (ftHidden in Typ);
  929.     if (Att and file_attribute_System) = file_attribute_System then
  930.       Result := Result and (ftSystem in Typ);
  931.     if (Att and file_attribute_Temporary) = file_attribute_Temporary then
  932.       Result := Result and (ftTemporary in Typ);
  933.   end
  934. end;
  935.  
  936. // Update File List with contents of the directory specified in
  937. // fDirectory
  938. procedure TFileList.UpdateFileList;
  939. var
  940.   OldViewStyle :TViewStyle;
  941.   I :integer;
  942.   // search single directory
  943.   procedure GetDirList (Dir :string);
  944.   var
  945.     Ret :boolean;
  946.     FindHandle :THandle;
  947.     FindData :TWin32FindData;
  948.     S :string;
  949.     ErrMode :integer;
  950.   begin
  951.     ErrMode := SetErrorMode(SEM_FAILCRITICALERRORS);// set error handler
  952.     FindHandle := Windows.FindFirstFile(PChar(Dir + '*'),FindData);
  953.     Ret := (FindHandle <> INVALID_HANDLE_VALUE);
  954.     try
  955.       while Ret do with FindData do
  956.       begin
  957.         if CheckAttributes (dwFileAttributes,fFileTypes) then
  958.         begin
  959.           S := FindData.cFileName;
  960.           if ((dwFileAttributes and file_attribute_Directory) > 0) then
  961.           begin
  962.             // adds directory entries only if required and not '.' or '..'
  963.             if fDisplayDir and (S <> '.') and (S <> '..') then
  964.             begin
  965.               AddFileData ((inherited AddFile (Dir + FindData.cFileName)),Dir,FindData) // Directory entry - add to list
  966.             end;
  967.           end
  968.           else
  969.           // test if file name matches mask
  970.           if CmpMask (AnsiUpperCase(FindData.cFileName),fFileMask,true,false) then
  971.           begin
  972.             AddFileData ((inherited AddFile (Dir + FindData.cFileName)),Dir,FindData); // adds matching file entry
  973.           end;
  974.         end;
  975.         Application.ProcessMessages;
  976.         Ret := Windows.FindNextFile(FindHandle,FindData) // get next entry
  977.       end;
  978.     finally
  979.       Windows.FindClose(FindHandle); // Close FindNext context
  980.       SetErrorMode(ErrMode); // Reset error handler
  981.     end;
  982.   end;
  983.  
  984. begin
  985.   OldViewStyle := ViewStyle;
  986.   Items.BeginUpdate;   // prevents video refresh until end of list loading
  987.   Items.Clear;         // clear list view
  988.   ViewStyle := vsList; // speeds up enumarating of contents
  989.   Screen.Cursor := crHourGlass; // set cursor shape
  990.   try
  991.     with fDirectory do
  992.       for I := 0 to Pred(Count) do
  993.       begin
  994.         GetDirList (Strings[I]);
  995.         Application.ProcessMessages;
  996.       end;
  997.   finally
  998.     // sort items by current sort item
  999.     CustomSort(@SortProc,fColumnInfos[SortColumn].Column); // Sorts on 0 based column index
  1000.     ViewStyle := OldViewStyle;
  1001.     Items.EndUpdate; // finally updates visual control
  1002.     Screen.Cursor := crDefault; // reset cursor
  1003.   end;
  1004. end;
  1005.  
  1006. ////////////////////////////////////////////////////////////////////////////////
  1007. // class registration
  1008. ////////////////////////////////////////////////////////////////////////////////
  1009. procedure Register;
  1010. begin
  1011.   RegisterComponents('Win95', [TFileView,TFileList]);
  1012. end;
  1013.  
  1014. end.
  1015.